package com.shujia.connctionpoolcheck;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class ConnectionPoolImpl implements ConnectionPool {

    // 未被使用的连接
    ArrayList<PoolElem> noUsedConnectionPool = new ArrayList<PoolElem>();

    // 使用的连接
    ArrayList<PoolElem> usedConnectionPool = new ArrayList<PoolElem>();

    ConnectionPoolConfig connectionPollConfig;

    // 读取到了所有的配置信息了

    public ConnectionPoolImpl(ConnectionPoolConfig connectionPollConfig) {
        this.connectionPollConfig = connectionPollConfig;
        // 获取一开始 连接池初始化的连接数量
        int initNum = connectionPollConfig.getInitNum();
        System.out.println("连接池正在创建...需要实例化" + initNum + "个连接");
        // 循环创建对应的连接
        for (int i = 0; i < initNum; i++) {
            PoolElem poolElem = createOneConnection(connectionPollConfig);
            noUsedConnectionPool.add(poolElem);
            System.out.println("第" + (i + 1) + "个连接创建成功...");
        }

        check();
    }

    @Override
    public synchronized PoolElem createOneConnection(ConnectionPoolConfig connectionPollConfig) {
        PoolElem poolElem = null;
        try {
            Class.forName(connectionPollConfig.getDriver());

            Connection connection = DriverManager.getConnection(
                    connectionPollConfig.getUrl()
                    , connectionPollConfig.getUser()
                    , connectionPollConfig.getPasswd()
            );

            poolElem = new PoolElem(connection, new Date());

        } catch (ClassNotFoundException | SQLException e) {
            throw new RuntimeException(e);
        }
        return poolElem;
    }

    @Override
    public synchronized Connection getConnection() {
        PoolElem poolElem = null;
        if (noUsedConnectionPool.size() > 0) {
            // 从未使用的连接池将连接移动至使用的连接池中
            poolElem = noUsedConnectionPool.remove(0);

            usedConnectionPool.add(poolElem);
            System.out.println(Thread.currentThread().getName() + "获取到一个连接");
            System.out.println("当前未使用的连接数量：" + noUsedConnectionPool.size() + " 正在使用的连接数量：" + usedConnectionPool.size());
        } else {
            // 如果连接池数量没有了，那么怎么办？
            // 1.什么情况下可以创建？
            int maxNum = connectionPollConfig.getMaxNum();
            if (usedConnectionPool.size() < 10) {
                poolElem = createOneConnection(connectionPollConfig);
                usedConnectionPool.add(poolElem);
                System.out.println("当前连接不足，并且没有达到上限，那么可以创建一个新的连接，并将其添加到正在使用的连接池中...");
                System.out.println("当前未使用的连接数量：" + noUsedConnectionPool.size() + " 正在使用的连接数量：" + usedConnectionPool.size());
            } else {
                // 2.什么情况下需要等待？
                try {
                    System.out.println("当前连接不足，并且达到上限了...，需要对获取的当前线程进行等待.." + Thread.currentThread().getName());
                    wait();
                    return getConnection();  // 当等待后，被唤醒可以继续执行代码，那么需要再调用自身获取对用的连接
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        }

        return poolElem.getConnection();
    }

    public void check() {

        if (connectionPollConfig.isScheduler()) {
            System.out.println("定时检查器正在开启...");
            Timer timer = new Timer();
            timer.schedule(new CheckTimer(this.noUsedConnectionPool, connectionPollConfig),
                    connectionPollConfig.getDelayTime() * 1000L,
                    connectionPollConfig.getPeriodTime() * 1000L
            );
        }else{
            System.out.println("连接池检查未被开启...");
        }
    }


    @Override
    public synchronized void releaseConnection(Connection connection) {
        // 获取到一个连接后，需要将该连接从 使用的连接池中 移动至 未使用的连接池中

        // 步骤：需要遍历使用的集合中每个PoolElem对象 并获取Connection 再去对比传入的Connection对象
        for (PoolElem poolElem : usedConnectionPool) {
            if (poolElem.getConnection() == connection) {
                // 先移除
                usedConnectionPool.remove(poolElem);


                // 对即将回收的连接进行做重置时间
                poolElem.resetStartDate();

                // 添加
                noUsedConnectionPool.add(poolElem);
                System.out.println(Thread.currentThread().getName() + "：当前线程释放了一个连接:" + connection);
                System.out.println("当前未使用的连接数量：" + noUsedConnectionPool.size() + " 正在使用的连接数量：" + usedConnectionPool.size());
                notify(); // 去唤醒一个没有获取到连接的线程

            }
        }
    }

    class CheckTimer extends TimerTask {
        ArrayList<PoolElem> noUsedConnectionPool;
        ConnectionPoolConfig connectionPollConfig;

        public CheckTimer(ArrayList<PoolElem> noUsedConnectionPool, ConnectionPoolConfig connectionPollConfig) {
            this.noUsedConnectionPool = noUsedConnectionPool;
            this.connectionPollConfig = connectionPollConfig;
        }

        @Override
        public void run() {
            // 开始检查
            System.out.println("开始检查当前连接池中，是否有超时的连接..");

            for (PoolElem poolElem : noUsedConnectionPool) {

                long startTime = poolElem.getDate().getTime();
                long endTime = System.currentTimeMillis();

                if ((endTime - startTime) / 1000 > connectionPollConfig.getOutTime()) {
                    // 如果达到超时时间，那么对Connection连接进行关闭
                    System.out.println("存在连接超时未使用,正在关闭.."+poolElem);
                    noUsedConnectionPool.remove(poolElem);
                    try {
                        poolElem.getConnection().close();
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
            }

        }
    }


}
